package com.cencat.common.utils;

import lombok.experimental.UtilityClass;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.*;
import org.springframework.web.client.RestTemplate;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import com.cencat.common.utils.CencatStringUtils;
import java.io.*;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * HTTP工具类
 * 提供HTTP请求、响应处理等功能
 * 
 * @author cencat
 * @since 2024-01-01
 */
@UtilityClass
public class HttpUtils {

    private static final Logger log = LoggerFactory.getLogger(HttpUtils.class);

    private static final RestTemplate restTemplate = new RestTemplate();
    private static final int DEFAULT_TIMEOUT = 30000; // 30秒



    /**
     * 发送GET请求
     * 
     * @param url 请求URL
     * @return 响应内容
     */
    public static String get(String url) {
        return get(url, null, null);
    }

    /**
     * 发送GET请求
     * 
     * @param url     请求URL
     * @param headers 请求头
     * @return 响应内容
     */
    public static String get(String url, Map<String, String> headers) {
        return get(url, headers, null);
    }

    /**
     * 发送GET请求
     * 
     * @param url     请求URL
     * @param headers 请求头
     * @param params  请求参数
     * @return 响应内容
     */
    public static String get(String url, Map<String, String> headers, Map<String, Object> params) {
        try {
            // 构建URL参数
            if (params != null && !params.isEmpty()) {
                url = buildUrlWithParams(url, params);
            }

            // 构建请求头
            HttpHeaders httpHeaders = new HttpHeaders();
            if (headers != null) {
                headers.forEach(httpHeaders::set);
            }

            HttpEntity<String> entity = new HttpEntity<>(httpHeaders);
            ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
            
            return response.getBody();
        } catch (Exception e) {
            log.error("GET请求失败: url={}, error={}", url, e.getMessage());
            return null;
        }
    }

    /**
     * 发送POST请求（JSON格式）
     * 
     * @param url  请求URL
     * @param data 请求数据
     * @return 响应内容
     */
    public static String postJson(String url, Object data) {
        return postJson(url, data, null);
    }

    /**
     * 发送POST请求（JSON格式）
     * 
     * @param url     请求URL
     * @param data    请求数据
     * @param headers 请求头
     * @return 响应内容
     */
    public static String postJson(String url, Object data, Map<String, String> headers) {
        try {
            // 构建请求头
            HttpHeaders httpHeaders = new HttpHeaders();
            httpHeaders.setContentType(MediaType.APPLICATION_JSON);
            if (headers != null) {
                headers.forEach(httpHeaders::set);
            }

            // 构建请求体
            String jsonData = JsonUtils.toJson(data);
            HttpEntity<String> entity = new HttpEntity<>(jsonData, httpHeaders);
            
            ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, entity, String.class);
            return response.getBody();
        } catch (Exception e) {
            log.error("POST JSON请求失败: url={}, error={}", url, e.getMessage());
            return null;
        }
    }

    /**
     * 发送POST请求（表单格式）
     * 
     * @param url    请求URL
     * @param params 请求参数
     * @return 响应内容
     */
    public static String postForm(String url, Map<String, Object> params) {
        return postForm(url, params, null);
    }

    /**
     * 发送POST请求（表单格式）
     * 
     * @param url     请求URL
     * @param params  请求参数
     * @param headers 请求头
     * @return 响应内容
     */
    public static String postForm(String url, Map<String, Object> params, Map<String, String> headers) {
        try {
            // 构建请求头
            HttpHeaders httpHeaders = new HttpHeaders();
            httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
            if (headers != null) {
                headers.forEach(httpHeaders::set);
            }

            // 构建表单数据
            StringBuilder formData = new StringBuilder();
            if (params != null) {
                for (Map.Entry<String, Object> entry : params.entrySet()) {
                    if (formData.length() > 0) {
                        formData.append("&");
                    }
                    formData.append(URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8.name()))
                            .append("=")
                            .append(URLEncoder.encode(String.valueOf(entry.getValue()), StandardCharsets.UTF_8.name()));
                }
            }

            HttpEntity<String> entity = new HttpEntity<>(formData.toString(), httpHeaders);
            ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, entity, String.class);
            
            return response.getBody();
        } catch (Exception e) {
            log.error("POST表单请求失败: url={}, error={}", url, e.getMessage());
            return null;
        }
    }

    /**
     * 发送PUT请求
     * 
     * @param url  请求URL
     * @param data 请求数据
     * @return 响应内容
     */
    public static String put(String url, Object data) {
        return put(url, data, null);
    }

    /**
     * 发送PUT请求
     * 
     * @param url     请求URL
     * @param data    请求数据
     * @param headers 请求头
     * @return 响应内容
     */
    public static String put(String url, Object data, Map<String, String> headers) {
        try {
            // 构建请求头
            HttpHeaders httpHeaders = new HttpHeaders();
            httpHeaders.setContentType(MediaType.APPLICATION_JSON);
            if (headers != null) {
                headers.forEach(httpHeaders::set);
            }

            // 构建请求体
            String jsonData = JsonUtils.toJson(data);
            HttpEntity<String> entity = new HttpEntity<>(jsonData, httpHeaders);
            
            ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.PUT, entity, String.class);
            return response.getBody();
        } catch (Exception e) {
            log.error("PUT请求失败: url={}, error={}", url, e.getMessage());
            return null;
        }
    }

    /**
     * 发送DELETE请求
     * 
     * @param url 请求URL
     * @return 响应内容
     */
    public static String delete(String url) {
        return delete(url, null);
    }

    /**
     * 发送DELETE请求
     * 
     * @param url     请求URL
     * @param headers 请求头
     * @return 响应内容
     */
    public static String delete(String url, Map<String, String> headers) {
        try {
            // 构建请求头
            HttpHeaders httpHeaders = new HttpHeaders();
            if (headers != null) {
                headers.forEach(httpHeaders::set);
            }

            HttpEntity<String> entity = new HttpEntity<>(httpHeaders);
            ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.DELETE, entity, String.class);
            
            return response.getBody();
        } catch (Exception e) {
            log.error("DELETE请求失败: url={}, error={}", url, e.getMessage());
            return null;
        }
    }

    /**
     * 下载文件
     * 
     * @param url      文件URL
     * @param filePath 保存路径
     * @return 是否成功
     */
    public static boolean downloadFile(String url, String filePath) {
        try {
            ResponseEntity<byte[]> response = restTemplate.getForEntity(url, byte[].class);
            if (response.getStatusCode() == HttpStatus.OK && response.getBody() != null) {
                File file = new File(filePath);
                File parentDir = file.getParentFile();
                if (parentDir != null && !parentDir.exists()) {
                    parentDir.mkdirs();
                }
                
                try (FileOutputStream fos = new FileOutputStream(file)) {
                    fos.write(response.getBody());
                    return true;
                }
            }
        } catch (Exception e) {
            log.error("下载文件失败: url={}, filePath={}, error={}", url, filePath, e.getMessage());
        }
        return false;
    }

    /**
     * 获取客户端IP地址
     * 
     * @param request HTTP请求
     * @return IP地址
     */
    public static String getClientIp(HttpServletRequest request) {
        if (request == null) {
            return "unknown";
        }

        String ip = request.getHeader("X-Forwarded-For");
        if (CencatStringUtils.isNotBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
            // 多次反向代理后会有多个IP值，第一个为真实IP
            int index = ip.indexOf(',');
            if (index != -1) {
                return ip.substring(0, index);
            } else {
                return ip;
            }
        }

        ip = request.getHeader("X-Real-IP");
        if (CencatStringUtils.isNotBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
            return ip;
        }

        ip = request.getHeader("Proxy-Client-IP");
        if (CencatStringUtils.isNotBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
            return ip;
        }

        ip = request.getHeader("WL-Proxy-Client-IP");
        if (CencatStringUtils.isNotBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
            return ip;
        }

        ip = request.getHeader("HTTP_CLIENT_IP");
        if (CencatStringUtils.isNotBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
            return ip;
        }

        ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        if (CencatStringUtils.isNotBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
            return ip;
        }

        return request.getRemoteAddr();
    }

    /**
     * 获取用户代理信息
     * 
     * @param request HTTP请求
     * @return 用户代理字符串
     */
    public static String getUserAgent(HttpServletRequest request) {
        if (request == null) {
            return "";
        }
        return request.getHeader("User-Agent");
    }

    /**
     * 判断是否为移动端请求
     * 
     * @param request HTTP请求
     * @return 是否为移动端
     */
    public static boolean isMobileRequest(HttpServletRequest request) {
        String userAgent = getUserAgent(request);
        if (CencatStringUtils.isBlank(userAgent)) {
            return false;
        }

        userAgent = userAgent.toLowerCase();
        return userAgent.contains("mobile") || 
               userAgent.contains("android") || 
               userAgent.contains("iphone") || 
               userAgent.contains("ipad") || 
               userAgent.contains("windows phone");
    }

    /**
     * 判断是否为Ajax请求
     * 
     * @param request HTTP请求
     * @return 是否为Ajax请求
     */
    public static boolean isAjaxRequest(HttpServletRequest request) {
        if (request == null) {
            return false;
        }
        
        String requestedWith = request.getHeader("X-Requested-With");
        return "XMLHttpRequest".equals(requestedWith);
    }

    /**
     * 设置响应头允许跨域
     * 
     * @param response HTTP响应
     */
    public static void setCorsHeaders(HttpServletResponse response) {
        if (response == null) {
            return;
        }
        
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
        response.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With");
        response.setHeader("Access-Control-Max-Age", "3600");
    }

    /**
     * 设置响应为JSON格式
     * 
     * @param response HTTP响应
     */
    public static void setJsonResponse(HttpServletResponse response) {
        if (response == null) {
            return;
        }
        
        response.setContentType("application/json;charset=UTF-8");
        response.setCharacterEncoding("UTF-8");
    }

    /**
     * 输出JSON响应
     * 
     * @param response HTTP响应
     * @param data     响应数据
     */
    public static void writeJsonResponse(HttpServletResponse response, Object data) {
        if (response == null) {
            return;
        }
        
        try {
            setJsonResponse(response);
            String json = JsonUtils.toJson(data);
            response.getWriter().write(json);
            response.getWriter().flush();
        } catch (Exception e) {
            log.error("输出JSON响应失败: {}", e.getMessage());
        }
    }

    /**
     * 构建带参数的URL
     * 
     * @param url    基础URL
     * @param params 参数
     * @return 完整URL
     */
    public static String buildUrlWithParams(String url, Map<String, Object> params) {
        if (CencatStringUtils.isBlank(url) || params == null || params.isEmpty()) {
            return url;
        }
        
        try {
            StringBuilder sb = new StringBuilder(url);
            if (!url.contains("?")) {
                sb.append("?");
            } else {
                sb.append("&");
            }
            
            boolean first = true;
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                if (!first) {
                    sb.append("&");
                }
                sb.append(URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8.name()))
                  .append("=")
                  .append(URLEncoder.encode(String.valueOf(entry.getValue()), StandardCharsets.UTF_8.name()));
                first = false;
            }
            
            return sb.toString();
        } catch (Exception e) {
            log.error("构建URL参数失败: {}", e.getMessage());
            return url;
        }
    }

    /**
     * 解析URL参数
     * 
     * @param url URL字符串
     * @return 参数Map
     */
    public static Map<String, String> parseUrlParams(String url) {
        Map<String, String> params = new HashMap<>();
        
        if (CencatStringUtils.isBlank(url)) {
            return params;
        }
        
        try {
            int questionIndex = url.indexOf('?');
            if (questionIndex == -1) {
                return params;
            }
            
            String queryString = url.substring(questionIndex + 1);
            String[] pairs = queryString.split("&");
            
            for (String pair : pairs) {
                String[] keyValue = pair.split("=", 2);
                if (keyValue.length == 2) {
                    String key = URLDecoder.decode(keyValue[0], StandardCharsets.UTF_8.name());
                    String value = URLDecoder.decode(keyValue[1], StandardCharsets.UTF_8.name());
                    params.put(key, value);
                }
            }
        } catch (Exception e) {
            log.error("解析URL参数失败: {}", e.getMessage());
        }
        
        return params;
    }

    /**
     * 验证URL格式
     * 
     * @param url URL字符串
     * @return 是否有效
     */
    public static boolean isValidUrl(String url) {
        if (CencatStringUtils.isBlank(url)) {
            return false;
        }
        
        try {
            new URL(url);
            return true;
        } catch (MalformedURLException e) {
            return false;
        }
    }

    /**
     * 获取URL的域名
     * 
     * @param url URL字符串
     * @return 域名
     */
    public static String getDomain(String url) {
        if (CencatStringUtils.isBlank(url)) {
            return "";
        }
        
        try {
            URL urlObj = new URL(url);
            return urlObj.getHost();
        } catch (MalformedURLException e) {
            log.error("获取URL域名失败: {}", e.getMessage());
            return "";
        }
    }

    /**
     * URL编码
     * 
     * @param str 待编码字符串
     * @return 编码后的字符串
     */
    public static String urlEncode(String str) {
        if (CencatStringUtils.isBlank(str)) {
            return str;
        }
        
        try {
            return URLEncoder.encode(str, StandardCharsets.UTF_8.name());
        } catch (UnsupportedEncodingException e) {
            log.error("URL编码失败: {}", e.getMessage());
            return str;
        }
    }

    /**
     * URL解码
     * 
     * @param str 待解码字符串
     * @return 解码后的字符串
     */
    public static String urlDecode(String str) {
        if (CencatStringUtils.isBlank(str)) {
            return str;
        }
        
        try {
            return URLDecoder.decode(str, StandardCharsets.UTF_8.name());
        } catch (UnsupportedEncodingException e) {
            log.error("URL解码失败: {}", e.getMessage());
            return str;
        }
    }
}